using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Shapes
{
    #region Enumerations

    public enum FillStyle
    {
        None, Solid, Image, Pattern, Gradient
    }

    public enum GradientAlignment
    {
        Default, Element, Form, Container, Client
    }

    public enum GradientStyle
    {
        Linear, ABALinear, Radial, Conical, Rectangular, Path
    }

    public enum ImageLayoutMode
    {
        Default, Stretched, Tiled
    }

    public enum ImageLayoutOrigin
    {
        Relative, Form, Container, Client
    }

    #endregion Enumerations

    /// <summary>
    /// Summary description for Appearance.
    /// </summary>
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class Appearance : System.ComponentModel.Component, ICloneable
    {
        // Background fill
        protected Fill background;

        // Border
        protected Border border;

        // Cursor
        protected System.Windows.Forms.Cursor cursor;

        // Foreground fill
        protected Fill foreground;

        // Rotation
        protected float rotation;

        // Shadow
        protected Shadow shadow;

        // Text
        protected System.Drawing.ContentAlignment textAlign;
        protected System.Drawing.Font textFont;
        protected float textRotation;
        protected System.Drawing.StringTrimming textTrimming;

        public Appearance()
        {
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Fill BackgroundFill
        {
            get { return background; }
            set
            {
                if (background != value)
                {
                    background.PropertyChanged -= new PropertyChangedEventHandler(this.OnSubObjectPropertyChanged);
                    background = value;
                    background.PropertyChanged += new PropertyChangedEventHandler(this.OnSubObjectPropertyChanged);
                }
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Fill ForegroundFill
        {
            get { return foreground; }
            set
            {
                if (foreground != value)
                {
                    foreground.PropertyChanged -= new PropertyChangedEventHandler(this.OnSubObjectPropertyChanged);
                    foreground = value;
                    foreground.PropertyChanged += new PropertyChangedEventHandler(this.OnSubObjectPropertyChanged);
                }
            }
        }

		[DefaultValue(0f)]
        public float Rotation
        {
            get { return rotation; }
            set { if (rotation != value) { rotation = value; OnPropertyChanged("Rotation"); } }
        }

        public object Clone()
        {
            return this.MemberwiseClone();
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, e);
        }

        protected void OnSubObjectPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (sender == background)
                OnPropertyChanged("BackgroundFill");
            else if (sender == foreground)
                OnPropertyChanged("ForegroundFill");
        }

        private void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
    }

    public class Border
    {
        protected DashStyle dashStyle;
        protected Fill fill;
        protected LineJoin lineJoin;
        protected System.Windows.Forms.Border3DStyle style;
        protected float width;

        internal Pen GetPen(GraphicsPath path, bool disabled)
        {
            if (fill.Style == FillStyle.None)
                return null;

            Pen p = new Pen(fill.GetBrush(path, disabled), width);
            p.DashStyle = dashStyle;
            p.Alignment = PenAlignment.Inset;
            p.LineJoin = lineJoin;
            return p;
        }
    }

    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class DrawnImage
    {
        protected Image image;

        //protected short alphaLevel;
        //protected ImageLayoutMode layout;
        //protected ImageLayoutOrigin origin; // ???
        //protected System.Drawing.ContentAlignment alignment;
        public Image Image
        {
            get { return image; }
            set { image = value; }
        }

        internal Brush GetBrush()
        {
            return new TextureBrush(image, WrapMode.Tile);
        }
    }

    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class Fill
    {
        internal ColorSet colors;

        protected Gradient gradient;
        protected DrawnImage image;
        protected Pattern pattern;
        protected FillStyle style;

        public Fill()
        {
            style = FillStyle.None;
            colors = new ColorSet();
            gradient = new Gradient();
            pattern = new Pattern();
            image = new DrawnImage();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public short AlphaLevel
        {
            get { return colors.alpha; }
            set { colors.alpha = value; }
        }

        public Color Color
        {
            get { return colors.color; }
            set { colors.color = value; }
        }

        public Color Color2
        {
            get { return colors.color2; }
            set { colors.color2 = value; }
        }

        public Color DisabledColor
        {
            get { return colors.disabledColor; }
            set { colors.disabledColor = value; }
        }

        public Color DisabledColor2
        {
            get { return colors.disabledColor2; }
            set { colors.disabledColor2 = value; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Gradient Gradient
        {
            get { return gradient; }
            set { gradient = value; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public DrawnImage Image
        {
            get { return image; }
            set { image = value; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Pattern Pattern
        {
            get { return pattern; }
            set { pattern = value; }
        }

        public FillStyle Style
        {
            get { return style; }
            set { style = value; }
        }

        internal Brush GetBrush(GraphicsPath path, bool disabled)
        {
            switch (style)
            {
                case FillStyle.None:
                    return null;
                case FillStyle.Solid:
                default:
                    return new SolidBrush(colors.GetColor(disabled));
                case FillStyle.Image:
                    return image.GetBrush();
                case FillStyle.Pattern:
                    return pattern.GetBrush(colors, disabled);
                case FillStyle.Gradient:
                    return gradient.GetBrush(colors, path, disabled);
            }
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, e);
        }
    }

    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class Gradient
    {
        //protected GradientAlignment alignment; // ???
        protected float angle, center;
        protected GradientStyle style;

        internal Brush GetBrush(ColorSet colors, GraphicsPath path, bool disabled)
        {
            Brush b = null;
            LinearGradientBrush lb;
            PathGradientBrush pgb;
            RectangleF bnds = path.GetBounds();
            Color c1 = colors.GetColor(disabled);
            Color c2 = colors.GetColor2(disabled);
            Blend blend = new Blend();
            blend.Factors = new float[] { 0f, 0.5f, 1f };
            blend.Positions = new float[] { 0f, (center + 1f)/2, 1f };

            switch (style)
            {
                case GradientStyle.Linear:
                default:
                    lb = new LinearGradientBrush(bnds, c1, c2, angle, true);
                    lb.GammaCorrection = true;
                    lb.Blend = blend;
                    b = lb;
                    break;
                case GradientStyle.ABALinear:
                    lb = new LinearGradientBrush(bnds, c1, c2, angle, true);
                    lb.GammaCorrection = true;
                    blend.Factors = new float[] { 0f, 1f, 0f };
                    blend.Positions = new float[] { 0f, (center + 1f)/2, 1f };
                    //blend.Factors = new float[] { 0f, 0f, 1f, 0f, 0f };
                    //blend.Positions = new float[] { 0f, 0.5f-((center+1f)/4f), 0.5f, 0.5f+((center+1f)/4f), 1f };
                    lb.Blend = blend;
                    b = lb;
                    break;
                case GradientStyle.Radial:
                case GradientStyle.Conical:
                    GraphicsPath radialPath = new GraphicsPath();
                    radialPath.AddEllipse(-1,-1,2,2);
                    pgb = new PathGradientBrush(radialPath);
                    float rad = (float)Math.Sqrt(Math.Pow(bnds.Width/2, 2) + Math.Pow(bnds.Height/2, 2));
                    pgb.ScaleTransform(rad, rad, MatrixOrder.Append);
                    pgb.TranslateTransform(bnds.Width/2f, bnds.Height/2f, MatrixOrder.Append);
                    goto PathGradientCommon;
                case GradientStyle.Rectangular:
                    GraphicsPath rectPath = new GraphicsPath();
                    rectPath.AddRectangle(bnds);
                    pgb = new PathGradientBrush(rectPath);
                    goto PathGradientCommon;
                case GradientStyle.Path:
                    pgb = new PathGradientBrush(path);
                    PathGradientCommon:
                    pgb.CenterColor = c2;
                    pgb.SurroundColors = new Color[] { c1 };
                    pgb.Blend = blend;
                    b = pgb;
                    break;
            }
            return b;
        }
    }

    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class Pattern
    {
        protected HatchStyle patternStyle;

        internal Brush GetBrush(ColorSet colors, bool disabled)
        {
            return new HatchBrush(patternStyle, colors.GetColor(disabled), colors.GetColor2(disabled));
        }
    }

    public class Shadow
    {
        protected short alphaLevel;
        protected Color color;
        protected Point offset;
        protected bool visible;
    }

    internal class ColorSet
    {
        public short alpha = 255; // scale 0 (transparent) - 255 (opaque)
        public Color color, color2, disabledColor, disabledColor2;

        public ColorSet()
        {
            color = color2 = Color.FromKnownColor(KnownColor.White);
            disabledColor = disabledColor2 = SystemColors.GrayText;
        }

        public Color GetColor(bool disabled)
        {
            return Color.FromArgb(alpha, disabled ? disabledColor : color);
        }

        public Color GetColor2(bool disabled)
        {
            return Color.FromArgb(alpha, disabled ? disabledColor2 : color2);
        }
    }
}